/*
 * Step_Two.cpp
 *
 *  Created on: 2011-6-17
 *      Author: ryan
 */

#include "Step_Two.h"

static int IPX[][2] = {{-1,0},{-1,-1},{-1,1},{-2,0},{-2,-1},{-2,1},{0,0}};
static int IPY[][2] = {{0,-1},{-1,-1},{1,-1},{0,-2},{-1,-2},{1,-2},{0,0}};

extern void gpu_initializeDeviceBuf(int);
extern void gpu_initCurStart(ACCM*);
extern void gpu_accmcalc(int, int, int);
extern void gpu_copyResult(ACCM*, int);
extern void gpu_switchPreAndCurLine();
extern void gpu_cudaFinalize();

void initialBuf(KEY_VALUE *kv) {
	int ri, i;

	refwidth = ref.width;
	refheight = ref.height;
	inpwidth = inp.width;
	inpheight = inp.height;

	int *p = kv->key;
	rank = p[0];
	task_start_point = p[1]-1;
	task_width = p[2]+1;
	task_height = refheight;
	sizer = task_width * task_height;
	sizei = sizeof(ACCM) * inpwidth * inpheight;

	//Initial match degree table;
	matchDegree = malloc(sizeof(ACCM*) * sizer);
	for(ri = 0; ri < sizer; ++ri){
		matchDegree[ri] = malloc(sizei);
	}

	//Initial pickup table;
	pickup = malloc(sizeof(POINT*) * refwidth);
	for(ri = 0; ri < refwidth; ++ri){
		pickup[ri] = malloc(sizeof(POINT) * refheight);
		memset(pickup[ri], 0, sizeof(POINT) * refheight);
	}

	//Initial pick table
	isPicked = malloc(sizeof(unsigned char*) * inpwidth);
	for(i = 0; i < inpwidth; ++i){
		isPicked[i] = malloc(sizeof(unsigned char) * inpheight);
		memset(isPicked[i], 0, sizeof(unsigned char) * inpheight);
	}
}

void nextCDP() {
	int ri, rj, gi, gj;
	//Initial cuda device buf
	int cpu_task_width, gpu_task_width, cpu_task_start, gpu_task_start;
	cpu_task_start = task_start_point;
	cpu_task_width = (task_width - 1) / DEVIDE;
	gpu_task_start = cpu_task_start + cpu_task_width;
	gpu_task_width = task_width - 1 - cpu_task_width;
	cpu_initializeDeviceBuf(++cpu_task_width);
	gpu_initializeDeviceBuf(++gpu_task_width);

	#pragma omp parallel sections
	{
		#pragma omp section
		{
			for (rj = 0; rj < task_height; ++rj) {
				if (rank > 0) {
					//1.
					RECV(matchDegree[rj], sizei, BYTE, PRE);
					//2.
					cpu_initCurStart(matchDegree[rj]);
				}
				//3.
				//cpu part
				for (ri = 1; ri < cpu_task_width; ++ri) {
					cpu_accmcalc(ri, rj, cpu_task_start);
				}
				for (ri = 1; ri < cpu_task_width; ++ri) {
					cpu_copyResult(matchDegree[ri * task_height + rj], ri);
				}
				cpu_switchPreAndCurLine();
				SEND(&rj, 1, INT, 0);
			}
		}
		//gpu part
		#pragma omp section
		{
			for (gj = 0; gj < task_height; ++gj) {
				int k;
				RECV(&k, 1, INT, 0);
				gpu_initCurStart(matchDegree[(cpu_task_width - 1) * task_height + gj]);
				for (gi = 1; gi < gpu_task_width; ++gi) {
					gpu_accmcalc(gi, gj, gpu_task_start);
				}
				for (gi = 1; gi < gpu_task_width; ++gi) {
					gpu_copyResult(matchDegree[(gi + cpu_task_width - 1) * task_height + gj], gi);
				}
				gpu_switchPreAndCurLine();
				if (rank < MACHINES - 1) {
					SEND(matchDegree[(task_width - 1) * task_height + gj], sizei, BYTE, NEXT);
				}
			}
		}
	}

	cpu_cudaFinalize();
	gpu_cudaFinalize();
}

void rankExtraction() {
	int i, j, x, y, min;

	min = INF;
	x = y = -1;
	ACCM *accm = matchDegree[task_width*task_height-1];
	for(i = 0; i < inpwidth; ++i){
		for(j = 0; j < inpheight; ++j){
			int d = accm->dxx + accm->dxy + accm->dyx + accm->dyy;
			if(min > d){
				min = d;
				x = i;
				y = j;
			}
			++accm;
		}
	}
	bestx = x;
	besty = y;
	pickup[refwidth-1][refheight-1].x = bestx;
	pickup[refwidth-1][refheight-1].y = besty;
}

void backTrace() {
	int i1, j1, i2, j2;
	int m1, n1, m2, n2, m3, n3, l1, l2;
	POINT *p, *p1, *p2, *p3, *pmn1, *pmn2;

	int xmin, ymin;
	int xcnt, ycnt;

	if (rank < MACHINES - 1) {
		for (i1 = task_start_point + task_width - 1; i1 < refwidth; ++i1) {
			RECV(pickup[i1], sizeof(POINT) * refheight, BYTE, NEXT);
		}
	}
	if(rank == MACHINES - 1){
		m1 = task_width - 1;
		for (n1 = task_height - 1; n1 > 0; --n1) {
			ymin = INF;
			ycnt = -1;
			pmn1 = &pickup[m1 + task_start_point][n1];
			for (l2 = 0; l2 < 7; ++l2) {
				i2 = pmn1->x + IPY[l2][0];
				j2 = pmn1->y + IPY[l2][1];
				if (i2 < 0 || i2 >= inpwidth || j2 < 0) {
					continue;
				}
				if (l2 == 6 && pmn1->y0 > LIMIT) {
					continue;
				}
				ACCM *accm = &matchDegree[m1*task_height+n1 - 1][i2*inpheight+j2];
				int d = accm->dxx + accm->dxy + accm->dyx + accm->dyy;
				if (ymin > d) {
					ymin = d;
					ycnt = l2;
				}
			}
			p = &pickup[m1 + task_start_point][n1 - 1];
			p1 = &pickup[m1 + task_start_point][n1];
			if (ycnt == -1 || ycnt == 6) {
				p->x = p1->x;
				p->y = p1->y;
				p->y0 = p1->y0 + 1;
			} else {
				p->x = p1->x + IPY[ycnt][0];
				p->y = p1->y + IPY[ycnt][1];
			}
		}
	}
	int end = 0;
	if (rank == 0) {
		end = 1;
	}
	n1 = task_height - 1;
	for(m1 = task_width - 1; m1 > end; --m1){
		xmin = INF;	xcnt = -1;
		pmn1 = &pickup[m1+task_start_point][n1];
		for(l1 = 0; l1 < 7; ++l1){
			i1 = pmn1->x + IPX[l1][0];
			j1 = pmn1->y + IPX[l1][1];
			if(i1 < 0 || j1 >= inpheight || j1 < 0){
				continue;
			}
			if(l1 == 6 && pmn1->x0 > LIMIT){
				continue;
			}
			ACCM *accm = &matchDegree[(m1-1)*task_height+n1][i1*inpheight+j1];
			int d = accm->dxx + accm->dxy + accm->dyx + accm->dyy;
			if(xmin > d){
				xmin = d;
				xcnt = l1;
			}
		}
		p = &pickup[m1+task_start_point-1][n1];
		p1 = &pickup[m1+task_start_point][n1];
		if(xcnt == -1 || xcnt == 6){
			p->x = p1->x;
			p->y = p1->y;
			p->x0 = p1->x0 + 1;
		}else{
			p->x = p1->x + IPX[xcnt][0];
			p->y = p1->y + IPX[xcnt][1];
		}
	}
	for(n1 = task_height - 2; n1 >= 0; --n1){
		for(m1 = task_width - 1; m1 > end; --m1){
			xcnt = -1; xmin = INF;
			m2 = m1-1;	n2 = n1 + 1;
			m3 = m1-1;	n3 = n1;
			pmn1 = &pickup[m1+task_start_point][n1];
			pmn2 = &pickup[m2 + task_start_point][n2];
			for (l2 = 0; l2 < 7; ++l2) {
				i2 = pmn2->x + IPY[l2][0];
				j2 = pmn2->y + IPY[l2][1];
				if (i2 < 0 || i2 >= inpwidth || j2 < 0) {
					continue;
				}
				if (l2 == 6 && pmn2->y0 > LIMIT) {
					continue;
				}
				for(l1 = 0; l1 < 7; ++l1){
					i1 = pmn1->x + IPX[l1][0];
					j1 = pmn1->y + IPX[l1][1];
					if(i1 != i2 || j1 != j2){
						continue;
					}
					if(l1 == 6 && pmn1->x0 > LIMIT){
						break;
					}
					ACCM *accm = &matchDegree[m3*task_height+n3][i1*inpheight+j1];
					int d = accm->dxx + accm->dxy + accm->dyx + accm->dyy;
					if(xmin > d){
						xmin = d;
						xcnt = l1;
					}
					break;
				}
			}
			p1 = &pickup[m1+task_start_point][n1];
			p2 = &pickup[m2+task_start_point][n2];
			p3 = &pickup[m3+task_start_point][n3];
			if(xcnt == -1 || xcnt == 6){
				p3->x = p1->x;
				p3->y = p1->y;
				p3->x0 = p1->x0+1;
			}else{
				p3->x = p1->x + IPX[xcnt][0];
				p3->y = p1->y + IPX[xcnt][1];
			}
		}
	}
	if (rank > 0) {
		for (i1 = task_start_point; i1 < refwidth; ++i1) {
			SEND(pickup[i1], sizeof(POINT) * refheight, BYTE, PRE);
		}
	}
}

void extractPic(){
	int i, j;
	for(j = 0; j < refheight; ++j){
		for(i = 0; i < refwidth; ++i){
			POINT *p = & pickup[i][j];
			color *c = &ref.data[i][j];
			if(c->r != 0 || c->g != 0 || c->b != 255){
				isPicked[p->x][p->y] = 1;
			}
		}
	}
}

void clearBuf() {
	int ri;
	for (ri = 0; ri < sizer; ++ri) {
		free(matchDegree[ri]);
	}
	free(matchDegree);
	matchDegree = NULL;

	for (ri = 0; ri < refwidth; ++ri) {
		free(pickup[ri]);
	}
	free(pickup);
	pickup = NULL;
}
